SyncSim manual
Contents
-
1.0 How to create a new Part
1.1 The Making of an AND-gate
1.2 The graphical representation of a Part
1.3 The Making of a register
-
2.0 Making a design file
2.1 The Simulator tag
2.2 The Component tag
1.0 How to create a new Part
The first step when writing a new part for SoftSim is
to identify if the part is combinatorial or synchronous. If the part is combinatorial
it should inherit SoftPart, otherwise it should inherit SoftSyncPart.
In SoftSim there is no difference between the two if you look at the
functions supported, but the simulator treats the two classes different internally.
The second step is to implement the required methods, which can be tricky if
you don’t know what you’re doing.
1.1 The Making of an AND-gate
Since an AND-gate is combinatorial we create a class according
to the naming convention and inherit SoftPart. We want the gate to
be as general as possible and to be able to do the and operation on
different bit widths is one way of increasing the flexibility of the part. Another
way could be to have n-number of inputs to the gate, but this example
will only go through the first of the two choices. The Part must have
a constructor that doesn't take any argument because the design Loader
can only create classes with an empty argument list.
import java.util.Properties;
import syncsim.Signal;
import syncsim.SoftPart;
public class AndPart extends SoftPart
{
protected final String PROP_BITWIDTH = "bitwidth"; // name of the property in the XML-file
protected int bitWidth; // Storing the bitWidth-property of the AND-gate
public AndPart() {
}
...
The class definition is ready and we know how the part is supposed
to work. Now we can start to define the inputs and outputs of the part. This
is done by inheriting the two methods in() and out() from
SoftPart. The methods doesn't take any arguments but they return a
String which contains the ports and the ports bit widths separated
by commas in the following format: name:width, name:width, ...
...
public String in() {
return "A:" + bitWidth + ", B:" + bitWidth;
}
public String out() {
return "out:"+bitWidth;
}
...
As shown in the code above, bitWidth is used when defining
the in- and out-ports of the part. When bitWidth chages, the port declaration
also changes. The next step is to implement the initialize method where
the value for bitWidth will be read from the XML-file.
...
public void initialize(String instanceName, Properties properties) {
this.instanceName = instanceName;
// read the PROP_BITWIDTH property and then remove it from the Properties object
String value = properties.getProperty( PROP_BITWIDTH );
properties.remove( PROP_BITWIDTH );
// the PROP_BITWIDTH property must be defined for this part to work, if it isn't throw an exception
if (value == null) {
throwMissingProperty( PROP_BITWIDTH );
}
// try to parse the value return from the Properties object into an integer
try {
bitWidth = Integer.parseInt(value);
} catch (NumberFormatException e) {
throwGeneralException( "Property '" + PROP_BITWIDTH + "' is not a number." );
}
// ensure that all properties defined in the XML-file was used
if (properties.size() > 0) {
throwUnknownProperties();
}
}
...
This style of the initialize method should be followed
to make the debugging of parts and designs easier. A misspelled 'bitwidth' in
the XML-file won't be detected by the Loader. To avoid such errors
you must check that all properties that were sent to the initialize
method were consumed. The methods throwMissingProperty, throwUnknownProperties
and throwGeneralException are defined in Part. All they do
is to throw well formatted RuntimeExceptions.
Now it's time to implement the actual logic of the AND-gate. For combinatorial
parts this is done in update whereas for synchronous parts it's done
in step (not the whole truth). The update method takes two arrays as
argument. The arrays are containing all signals on the ports of the part and
the method is supposed to do all logic in this method.
...
public void update(Signal[] in, Signal[] out) {
// only preform the logic if the insignals are valid
if ( in[0].isValid() && in[1].isValid() ) {
out[0] = new Signal(bitWidth, in[0].and(in[1]));
}
}
}
That's it, now we have a working AND-gate for the SoftSim
simulator. The method isn't very hard to understand. First the in signals are
checked so that they are valid. Then the AND-operation is performed by calling
the and method from Signals superclass BigInteger
and uses that result to create a new Signal. The Signals in
SoftSim is constant and can't be changed, the only way to change the
value of a port is to create a new Signal.
The source for AndPart.java or view
it here
1.2 The graphical representation of a Part
In the previous section we learned how to create a combinatorial
part for the SoftSim simulator. That's one step in the right direction
but we also need some way to represent the gate in the simulator GUI. The answer
is to search for already finished graphical representations and use one of them,
or you can make you own by inheriting SSComponent. To make a simple
graphical component you only have to implement the paint method. But if you
want to have other tooltips or some sort of communication between your simulator
part and your graphical component you'll have to implement more methods.
import java.awt.*;
import syncsim.SSComponent;
public class AndComponent extends SSComponent {
public AndComponent() {
}
public void paint(Graphics g) {
g.setColor(Color.black);
g.drawRect(0, 0, getWidth()-1, getHeight()-1);
}
}
This component only draws a rectangle that’s the size of
the component. It’s preferable to make the graphics scalable because components
can re resized in the XML-file.
The source for AndComponent.java or view
it here
1.3 The Making of a register
Registers are synchronous and must therefore be derived from
SoftSyncPart. The making of a register is basically the same as making
an AND-gate, only a few things are different. We first make our class declaration
then declare ports and finally the value for bitWidth is fetched.
import java.util.Properties;
import syncsim.SoftSyncPart;
import syncsim.Signal;
public class RegisterPart extends SoftSyncPart {
private static final String PROP_BITWIDTH = "bitwidth";
private int bitWidth;
private Signal prevSignal; // representing the registers "memory"
public RegisterPart() {
}
public String in() {
return "in:" + bitWidth;
}
public String out() {
return "out:" + bitWidth;
}
public void initialize(String instanceName, Properties properties) {
super.initialize(instanceName, properties);
String s = properties.getProperty(PROP_BITWIDTH);
properties.remove(PROP_BITWIDTH);
if (s == null) {
throwMissingProperty(PROP_BITWIDTH);
}
try {
bitWidth = Integer.parseInt(s);
}
catch (NumberFormatException e) {
throwGeneralException("Property '" + PROP_BITWIDTH + "' is not a number.");
}
if (properties.size() > 0) {
throwUnknownProperties();
}
}
...
Nothing new there except that the Part is inheriting SoftSyncPart
instead of SoftPart. In synchronous parts, both the update
and the step method must be implemented. Step is the method
that should calculate the output signals, while update only is used
for refreshing the outputs.
public void getOutput(Signal[] out) {
out[0] = prevSignal;
}
public void step(Signal[] in, Signal[] out) {
prevSignal = in[0];
getOutput(out);
}
public void update(Signal[] in, Signal[] out) {
getOutput(out);
}
}
Each time step is called, the register saves the input
signal and redirects it to the out port. When update is called the
same signal is once again set at the out port. This saves a lot of calculations
since update is called much more than step.
The source for RegisterPart.java or view
it here
2.0 Making a design file
Designs are stored in an XML-file that can be loaded from the
GUI. The file defines which parts that are going to be used, which graphical
representation they will have and how they are connected.
<?xml version='1.0' encoding='UTF-8' ?>
<design>
<simulator class="syncsim.SoftSim">
<property Register:in="Register:out" />
</simulator>
<component name="Register" class="syncsim.RegisterPart">
<gfx class="syncsim.MemoryComponent" width="40" height="25" x="100" y="100">
</gfx>
<property bitwidth="32" />
</component>
</design>
The code above is a 32 bit register with its output connected
to its input. This sort of connection isn't very meaningfull it is only an example
of how the syntax looks. The first tag in the XML-file is the design
tag where the whole design should be defined.
2.1 The Simulator tag
The simulator tag specifies which simulator
is used when running the design file. Properties can be defined within
all tags. These properties are sent to the initialize method
of the class instance that the tag represent. In example above SoftSim
gets the strange property Register:in="Register:out". In SoftSim
this means that the port named 'in' of the part 'Register' will be connected
to the port 'out' on the part 'Register'.
2.2 The Component tag
Simulator components are defined with a component
tag. The class that is loaded in the component tag isn't anything that will
show up in the GUI, it is only used by the simulator. If you want a
graphical representation of the part you will need to declare one or more gfx
tags. For example a wire is not always a straight line, it might go around a
part to make it easier to follow. To avoid declaring several simulator
components with one graphical component each, you can declare one simulator
component and many graphical components.
SyncSim 2004